<?php
require_once( 'tsConfig.class.php' );

/**
 * Database container class for session data
 *
 * Create the following table to store session data
 * <code>
 * CREATE TABLE `sessions` (
 *     `id` varchar(32) NOT NULL,
 *     `data` text,
 *     `remote` varchar(64),
 *     `server` varchar(64),
 *     `updated` datetime NOT NULL,
 *     PRIMARY KEY ( `id` )
 * );
 * </code>
 *
 * @author K.Tamura <tamura@terras.co.jp>
 */
class tsSession
{

    public $conn = null;
    public $table = 'sessions';
    public $row = array();
    public $is_regenerate_id = false;

    public $handler = 'files';
    public $path = '';

    public function __construct( $handler = 'files', $path = null )
    {
        if( null !== $handler )
        {
            $this->handler = $handler;
        }
        if( null !== $path )
        {
            $this->path = $path;
        }
    }

    public function start()
    {
        ini_set( 'session.save_handler', $this->handler );
        ini_set( 'session.save_path', $this->path );

        $config = tsConfig::getInstance( CONF_FILE );

        ini_set( 'session.gc_probability', $config->get( 'SESSION', 'gc_probability', 1 ) );
        ini_set( 'session.gc_divisor', $config->get( 'SESSION', 'gc_divisor', 100 ) );
        // gc execute rate is gc_probability/gc_divisor (default: 1/100)
        ini_set( 'session.gc_maxlifetime', $config->get( 'SESSION', 'gc_maxlifetime', 1440 ) );
        ini_set( 'session.cache_limiter', 'none' );
        ini_set( 'session.hash_function', 1 );
        ini_set( 'session.hash_bits_per_character', 5 );

        // set session name
        //
        $name = $config->get( 'SESSION', 'name' );
        if( null !== $name )
        {
            session_name( $name );
        }

        // set session handler
        //
        if( 'user' == $this->handler )
        {
            // Set custom handlers
            session_set_save_handler(
                array( $this, "open" ),
                array( $this, "close" ),
                array( $this, "read" ),
                array( $this, "write" ),
                array( $this, "destroy" ),
                array( $this, "gc" )
            );
        }

        // start session ...
        //
        session_start();

        // realy valid session id ?
        //
        if( true !== self::isRegistered() )
        {
            // clean session datas & restart session
            $_SESSION = array();
            session_write_close();
            session_start();
        }

        // regenerate session id
        //
        if( $this->is_regenerate_id )
        {
            session_regenerate_id( true );
        }
    }

    public static function isRegistered( $id = null )
    {
        $bool = false;

        if( null === $id ) $id = session_id();

        if( 'user' == session_module_name() )
        {
            /**
             * @todo search session db
             */
        }
        else
        {
            $sess_file = 'sess_' . $id;
            $sess_dir = scandir( session_save_path() );
            if( true === in_array( $sess_file, $sess_dir ) )
            {
                $bool = true;
            }
        }

        return $bool;
    }

    public function open( $save_path, $sess_name )# do not modify function parameters
    {
        $parsed = parse_url( $save_path );
        $host = $parsed['host'];
        $user = $parsed['user'];
        $pass = @$parsed['pass'];
        $dbname = trim( $parsed['path'], '/' );

        $this->conn = mysql_pconnect( $host, $user, $pass );
        $res = mysql_select_db( $dbname, $this->conn );
        if( ! $res )
        {
            return false;
        }

        return true;
    }

    public function read( $id )# do not modify function parameters
    {
        $query = "SELECT `data` FROM `" . $this->table . "` WHERE `id` = '" . $id . "'";

        $res = mysql_query( $query, $this->conn );
        if( @mysql_num_rows( $res ) != 1 )
        {
            return "";# must return string, not 'false'
        }

        $this->row = mysql_fetch_assoc( $res );
        $this->row["data"] = base64_decode( $this->row["data"] );

        return $this->row["data"];
    }

    public function write( $id, $data )# do not modify function parameters
    {
        if( ! $data ) return false;

        $data = mysql_real_escape_string( base64_encode( $data ) );
        if( $this->row )
        {
            $query  = "UPDATE `" . $this->table . "`";
            $query .= " SET `data` = '" . $data . "', `updated` = NOW()";
            $query .= " WHERE `id` = '" . $id . "'";
        }
        else
        {
            $query  = "INSERT INTO `" . $this->table . "`";
            $query .= " SET `id` = '" . $id . "', `data` = '" . $data . "', `updated` = NOW()";
            $query .= ", `remote` = '" . $_SERVER['REMOTE_ADDR'] . "'";
            $query .= ", `server` = '" . $_SERVER['SERVER_ADDR'] . "'";
        }

        $res = mysql_query( $query, $this->conn );
        if( ! $res )
        {
            return false;
        }
        return true;
    }

    public function close()
    {
        $res = mysql_close( $this->conn );
        if( ! $res )
        {
            return false;
        }
        return true;
    }

    public function destroy( $id )# do not modify function parameters
    {
        $query = "DELETE FROM `" . $this->table . "` WHERE `id` = '" . $id . "'";

        $res = mysql_query( $query, $this->conn );
        if( ! $res )
        {
            return false;
        }
        return true;
    }

    public function gc( $lifetime )
    {
        $expire = date( 'Y-m-d H:i:s', time() - $lifetime );
        $query = "DELETE FROM `" . $this->table . "` WHERE `updated` < '" . $expire . "'";

        $res = mysql_query( $query, $this->conn );
        if( ! $res )
        {
            return false;
        }
        return true;
    }

}